﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Handrise.Mobile.BasicModal.GPS
{
    /// <summary>
    /// 定时接收GPS信息事件委托
    /// </summary>
    /// <param name="position"></param>
    public delegate void ReadGPSInfoHandler(GpsInformation position);

    /// <summary>
    /// 设备状态改变事件委托
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    public delegate void DeviceStateChangedEventHandler(object sender, GpsWorkState args);

    /// <summary>
    /// 高斯投影中所选用的参考椭球
    /// </summary>
    public enum GaussSphere
    {
        Beijing54,
        Xian80,
        WGS84,
    }

    /// <summary>
    /// GPS工作状态

    /// </summary>
    public enum GpsWorkState
    {
        Position,
        Close,
        Positioning
    }

    public class GPSService
    {


        /// <summary>
        ///  Gps设备的句柄        /// </summary>
        IntPtr gpsHandle = IntPtr.Zero;

        /// <summary>
        /// 设备状态改变的句柄
        /// </summary>
        IntPtr deviceStateHandle = IntPtr.Zero;

        /// <summary>
        /// 计时器
        /// </summary>
        private Timer getGpsInfoTimer;

        /// <summary>
        ///当前GPS状态
        /// </summary>
        public GpsWorkState GpsWorkState
        {
            get { return gpsWorkState; }
        }

        /// <summary>
        /// 当前GPS状态
        /// </summary>
        GpsWorkState gpsWorkState = GpsWorkState.Close;

        /// <summary>
        /// 设备状态改变的事件
        /// </summary>
        public event DeviceStateChangedEventHandler OnDeviceStateChange;


        /// <summary>
        /// 实时获取GPS信息事件        /// </summary>
        private event ReadGPSInfoHandler OnReadGPSInfo;


        /// <summary>
        /// 计数器        /// </summary>
        public int counter = 0;

        /// <summary>
        /// 旧的位置信息
        /// </summary>
        private GpsInformation oldgGpsInfo = new GpsInformation();


        /// <summary>
        /// 构造函数
        /// </summary>
        public GPSService()
        {
            getGpsInfoTimer = new Timer();
            getGpsInfoTimer.Interval = 2000;
            getGpsInfoTimer.Tick += new EventHandler(getPositionTimer_Tick);
            getGpsInfoTimer.Enabled = false;
        }

        /// <summary>
        /// 打开 GPS，0：已打开,1：无GPS硬件,2：GPS已打开
        /// </summary>
        /// <returns>0：已打开,1：无GPS硬件,2：GPS已打开</returns>
        public int Open()
        {
            if (gpsWorkState == GpsWorkState.Close)
            {

                deviceStateHandle = GPSAPI.CreateEvent(IntPtr.Zero, 0, 0, null);

                try
                {
                    //gpsHandle = GPSAPI.GPSOpenDevice(newLocationHandle, deviceStateChangedHandle, null, 0);
                    gpsHandle = GPSAPI.GPSOpenDevice(IntPtr.Zero, deviceStateHandle, null, 0);

                    if (gpsHandle != IntPtr.Zero)
                    {
                        GpsDeviceState device = GetDeviceState();
                        if (device == null || device.DeviceState == GpsServiceState.Unknown)
                            return 1;
                        else
                        {
                            getGpsInfoTimer.Enabled = true;
                            gpsWorkState = GpsWorkState.Positioning;
                            if (OnDeviceStateChange != null)
                                OnDeviceStateChange(this, gpsWorkState);
                            return 0;
                        }
                    }
                    else
                    {
                        return 1;
                    }

                }
                catch
                {
                    return 1;
                }
            }
            else
            {
                return 2;
            }
        }

        /// <summary>
        /// 关闭GPS,返回为0：关闭成功，返回为1：GPS未打开
        /// </summary>
        public int Close()
        {
            if (gpsWorkState != GpsWorkState.Close)
            {
                GPSAPI.GPSCloseDevice(gpsHandle);
                getGpsInfoTimer.Enabled = false;
                gpsHandle = IntPtr.Zero;
                gpsWorkState = GpsWorkState.Close;
                counter = 0;
                if (OnDeviceStateChange != null)
                    OnDeviceStateChange(this, gpsWorkState);
                return 0;
            }
            else
            {
                return 1;
            }
        }

        void getPositionTimer_Tick(object sender, EventArgs e)
        {
            getGpsInfoTimer.Enabled = false;
            GpsInformation gpsPosition = null;
            try
            {
                gpsPosition = GetGpsInfo();

                if (gpsPosition == null || (gpsPosition.DoubleLatitude == oldgGpsInfo.DoubleLatitude &&
                    gpsPosition.DoubleLongtitude == oldgGpsInfo.DoubleLongtitude))
                {
                    counter += getGpsInfoTimer.Interval;
                    if (counter > 60 * 1000)  //40秒内没有收到数据，重起服务 
                    {
                        GPSAPI.GPSCloseDevice(gpsHandle);
                        gpsHandle = IntPtr.Zero;
                        gpsHandle = GPSAPI.GPSOpenDevice(IntPtr.Zero, deviceStateHandle, null, 0);
                        counter = 0;
                        if (gpsWorkState == GpsWorkState.Position)
                        {
                            gpsWorkState = GpsWorkState.Positioning;
                            if (OnDeviceStateChange != null)
                                OnDeviceStateChange(this, gpsWorkState);
                        }
                    }
                }
                else
                {
                    oldgGpsInfo = gpsPosition;
                    counter = 0;
                    if (gpsWorkState != GpsWorkState.Position)
                    {
                        gpsWorkState = GpsWorkState.Position;
                        if (OnDeviceStateChange != null)
                            OnDeviceStateChange(this, gpsWorkState);
                    }
                }
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
            }
            finally
            {
                getGpsInfoTimer.Enabled = true;
                if (OnReadGPSInfo != null)
                    OnReadGPSInfo(gpsPosition);
            }

        }

        private GpsInformation GetGpsInfo()
        {
            GpsInformation gpsPosition = null;
            if (gpsWorkState != GpsWorkState.Close)
            {
                try
                {
                    // allocate the necessary memory on the native side.  We have a class (GpsPosition) that 
                    // has the same memory layout as its native counterpart
                    IntPtr ptr = Utils.LocalAlloc(Marshal.SizeOf(typeof(GpsInformation)));

                    // fill in the required fields 
                    gpsPosition = new GpsInformation();
                    gpsPosition.dwVersion = 1;
                    gpsPosition.dwSize = Marshal.SizeOf(typeof(GpsInformation));

                    // Marshal our data to the native pointer we allocated.
                    Marshal.StructureToPtr(gpsPosition, ptr, false);

                    // call native method passing in our native buffer
                    int result = GPSAPI.GPSGetPosition(gpsHandle, ptr, 500000, 0);
                    if (result == 0)
                    {
                        // native call succeeded, marshal native data to our managed data
                        gpsPosition = (GpsInformation)Marshal.PtrToStructure(ptr, typeof(GpsInformation));
                    }

                    // free our native memory
                    Utils.LocalFree(ptr);
                }
                catch (Exception ex)
                {
                    string msg = ex.Message;
                }
            }
            return gpsPosition;
        }

        /// <summary>
        /// 获得当前GPS坐标,设备关闭或者没有定位时返回null
        /// </summary>
        /// <returns>设备关闭或者没有定位时返回null</returns>
        public GpsInformation GetGpsInformation(out GpsWorkState GpsWorkState)
        {
            GpsWorkState = this.gpsWorkState;
            if (gpsWorkState != GpsWorkState.Position)
            {
                return null;
            }

            return GetGpsInfo();
        }

        /// <summary>
        /// 获得当前GPS坐标,设备关闭或者没有定位时返回null
        /// </summary>
        /// <returns></returns>
        public GpsInformation GetGpsInformation()
        {
            return GetGpsInfo();
        }

        /// <summary>
        /// 计算两个坐标点的距离
        /// </summary>
        /// <param name="lng1">第一个经度</param>
        /// <param name="lat1">第一个纬度</param>
        /// <param name="lng2">第一个经度</param>
        /// <param name="lat2">第一个纬度</param>
        /// <param name="gs">高斯投影中所选用的参考椭球</param>
        /// <returns>返回距离,返回-1时经纬度格式错误</returns>
        public double DistanceOfTwoPoints(double lng1, double lat1, double lng2, double lat2, GaussSphere gs)
        {
            return GetDistance.DistanceOfTwoPoints(lng1, lat1, lng2, lat2, gs);
        }

        /// <summary>
        /// 计算两个坐标点的距离，默认高斯投影中所选用的参考椭球为WGS84
        /// </summary>
        /// <param name="lng1">第一个经度</param>
        /// <param name="lat1">第一个纬度</param>
        /// <param name="lng2">第一个经度</param>
        /// <param name="lat2">第一个纬度</param>
        /// <returns>返回距离,返回-1时经纬度格式错误</returns>
        public double DistanceOfTwoPoints(double lng1, double lat1, double lng2, double lat2)
        {

            return GetDistance.DistanceOfTwoPoints(lng1, lat1, lng2, lat2, GaussSphere.WGS84);
        }

        public GpsDeviceState GetDeviceState()
        {
            GpsDeviceState device = null;

            // allocate a buffer on the native side.  Since the
            IntPtr pGpsDevice = Utils.LocalAlloc(GpsDeviceState.GpsDeviceStructureSize);

            // GPS_DEVICE structure has arrays of characters, it's easier to just
            // write directly into memory rather than create a managed structure with
            // the same layout.
            Marshal.WriteInt32(pGpsDevice, 1);	// write out GPS version of 1
            Marshal.WriteInt32(pGpsDevice, 4, GpsDeviceState.GpsDeviceStructureSize);	// write out dwSize of structure

            int result = GPSAPI.GPSGetDeviceState(pGpsDevice);

            if (result == 0)
            {
                // instantiate the GpsDeviceState class passing in the native pointer
                device = new GpsDeviceState(pGpsDevice);
            }

            // free our native memory
            Utils.LocalFree(pGpsDevice);

            return device;
        }


    }
}
